package jmat.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

public class Discrete_Bayes_wo {
	int[] discreteBayes(int[][] trainPatterns, ArrayList<Integer> trainTargets, int[][] testPatterns){
		TreeSet<Integer> set = new TreeSet<Integer>(trainTargets);
		Iterator<Integer> iterator = set.iterator();
		ArrayList<Integer> uc = new ArrayList<Integer>(set.size());
		while(iterator.hasNext()){
			uc.add(iterator.next());
		}
		ArrayList<ArrayList<Integer>> uniquePatterns = new ArrayList<ArrayList<Integer>>();
		ArrayList<ArrayList<Integer>> counts = new ArrayList<ArrayList<Integer>>();
		for(int i = 0; i < trainPatterns[0].length; ++i){
			int[] data = new int[trainPatterns.length];
			for(int j = 0; j < data.length; ++j){
				data[j] = trainPatterns[j][i];
			}
			int[] sums = new int[trainPatterns[0].length];
			for(int j = 0; j < sums.length; ++j){
				for(int k = 0; k < data.length; ++k){
					sums[j] += data[k] - trainPatterns[k][j];
				}
			}
			ArrayList<Integer> filter = new ArrayList<Integer>();
			for(int j = 0; j < sums.length; ++j){
				if(sums[j] == 0){
					filter.add(trainTargets.get(j));
				}
			}
			if(uniquePatterns.isEmpty()){
				uniquePatterns.ensureCapacity(data.length);
				for(int j = 0; j < data.length; ++j){
					ArrayList<Integer> list = new ArrayList<Integer>();
					list.add(data[j]);
					uniquePatterns.add(list);
				}
				counts.ensureCapacity(uc.size());
				for(Integer val : uc){
					int count = 0;
					for(Integer cand : filter){
						if(cand == val){
							++count;
						}
					}
					ArrayList<Integer> list = new ArrayList<Integer>();
					list.add(count);
					counts.add(list);
				}
			} else{
				boolean neq = true;
				for(int j = 0; j < uniquePatterns.size(); ++j){
					ArrayList<Integer> list = uniquePatterns.get(j);
					for(Integer val : list){
						neq &= val != data[j];
					}
				}
				if(neq){
					for(int j = 0; j < data.length; ++j){
						uniquePatterns.get(j).add(data[j]);
					}
					for(int j = 0; j < uc.size(); ++j){
						int count = 0;
						Integer val = uc.get(j);
						for(Integer cand : filter){
							if(cand == val){
								++count;
							}
						}
						counts.get(j).add(count);
					}
				}
			}
		}
		ArrayList<ArrayList<Double>> pX = new ArrayList<ArrayList<Double>>(counts.size());
		for(int i = 0; i < counts.size(); ++i){
			ArrayList<Integer> line = counts.get(i);
			Double lineSum = 0.0;
			for(Integer val : line){
				lineSum += val;
			}
			ArrayList<Double> list = new ArrayList<Double>(line.size());
			for(Integer val : line){
				list.add(val / lineSum);
			}
			pX.add(list);
		}
		ArrayList<Integer> uniqueTargets = new ArrayList<Integer>(pX.get(0).size());
		for(int i = 0; i < pX.get(0).size(); ++i){
			Double max = Double.MIN_VALUE;
			int index = 0;
			for(int j = 0; j < pX.size(); ++j){
				Double val = pX.get(j).get(i);
				if(val > max){
					max = val;
					index = j;
				}
			}
			uniqueTargets.add(index);
		}
		
		return nearestNeighbor(uniquePatterns, uniqueTargets, testPatterns, 1);
	}
	
	int[] nearestNeighbor(ArrayList<ArrayList<Integer>> trainPatterns, ArrayList<Integer> trainTargets, int[][] testPatterns, int knn){
		TreeSet<Integer> set = new TreeSet<Integer>(trainTargets);
		Iterator<Integer> iterator = set.iterator();
		ArrayList<Integer> uc = new ArrayList<Integer>(set.size());
		while(iterator.hasNext()){
			uc.add(iterator.next());
		}
		
		if(trainTargets.size() < knn){
			throw new IllegalArgumentException("You specified more neighbors than there are points.");
		}
		
		int n = testPatterns[0].length;
		int[] testTargets = new int[n];
		for(int i = 0; i < n; ++i){
			int[][] mat = new int[trainTargets.size()][n];
			for(int j = 0; j < mat.length; ++j){
				for(int k = 0; k < mat[0].length; ++k){
					int val = trainPatterns.get(j).get(k) - testPatterns[j][i];
					mat[j][k] = val * val;
				}
			}
			int[] sum = new int[n];
			for(int j = 0; j < mat.length; ++j){
				for(int k = 0; k < n; ++k){
					sum[k] += mat[j][k];
				}
			}
			ArrayList<Integer> dist = new ArrayList<Integer>(n);
			for(int j = 0; j < n; ++j){
				dist.add(sum[j]);
			}
			TreeSet<Integer> distSet = new TreeSet<Integer>(dist);
			Iterator<Integer> distIterator = distSet.iterator();
			ArrayList<Integer> uq = new ArrayList<Integer>(distSet.size());
			while(distIterator.hasNext()){
				uq.add(distIterator.next());
			}
			ArrayList<Integer> indices = new ArrayList<Integer>(dist.size());
			for(Integer val : uq){
				int index = dist.indexOf(val);
				indices.add(index);
				List<Integer> subList;
				while(index != dist.lastIndexOf(val)){
					subList = indices.subList(index + 1, dist.size());
					index = subList.indexOf(val);
					indices.add(index);
				}
			}
			ArrayList<Integer> filter = new ArrayList<Integer>(knn);
			for(Integer index : indices.subList(0, knn)){
				filter.add(trainTargets.get(index));
			}
			ArrayList<Integer> hist = new ArrayList<Integer>(uc.size());
			for(Integer val : uc){
				int count = 0;
				for(Integer cand : filter){
					if(val == cand){
						++count;
					}
				}
				hist.add(count);
			}
			int max = Integer.MIN_VALUE;
			for(int j = 0; j < hist.size(); ++j){
				if(hist.get(j) > max){
					max = hist.get(j);
					testTargets[i] = j;
				}
			}
		}
		return testTargets;
	}
}
